/*!
 * \file WtStraDtSel.cpp
 * \project WonderTrader
 *
 * \author Wesley
 * \date 2020/03/30
 * 
 * \brief WonderTrader 数据选股策略实现文件
 * 
 * \details 该文件实现了WtStraDtSel类的完整功能，提供基于DualThrust算法的选股策略：
 *          
 *          核心选股算法：
 *          1. 多品种并行分析：同时分析配置的所有候选品种
 *          2. DualThrust指标计算：
 *             - HH = N天内最高价的最大值
 *             - LL = N天内最低价的最小值  
 *             - HC = N天内收盘价的最大值
 *             - LC = N天内收盘价的最小值
 *             - Range = max(HH-LC, HC-LL)
 *          3. 选股判断标准：
 *             - 上轨 = 开盘价 + K1 * Range
 *             - 下轨 = 开盘价 - K2 * Range
 *             - 突破上轨：建立多头持仓
 *             - 跌破下轨：建立空头持仓（期货）
 *             - 反向突破：平仓出场
 *          4. 组合管理：
 *             - 单品种持仓：每个品种独立管理
 *             - 风险控制：股票仅做多，期货可双向
 *             - 动态调整：根据信号实时调整组合
 *          
 *          选股策略特性：
 *          - 多市场适应：同时支持股票和期货市场
 *          - 分散投资：通过多品种降低单一风险
 *          - 量化决策：完全基于数据和算法的客观决策
 *          - 实时响应：根据市场变化及时调整持仓
 *          
 *          技术实现：
 *          - 高效遍历：批量处理多个品种提高效率
 *          - 内存管理：及时释放K线数据防止内存泄漏
 *          - 异常处理：完善的错误检查和状态管理
 *          - 交易时间验证：确保在正确的交易时段执行
 */

#include "WtStraDtSel.h"

#include "../Includes/ISelStraCtx.h"

#include "../Includes/WTSContractInfo.hpp"
#include "../Includes/WTSSessionInfo.hpp"
#include "../Includes/WTSVariant.hpp"
#include "../Includes/WTSDataDef.hpp"
#include "../Share/decimal.h"
#include "../Share/StrUtil.hpp"
#include "../Share/fmtlib.h"

extern const char* FACT_NAME;

/*!
 * \brief 构造函数
 * \param id 策略实例唯一标识符
 * 
 * \details 初始化DualThrust选股策略实例：
 *          - 调用基类构造函数设置策略ID
 *          - 成员变量将在init方法中从配置文件初始化
 */
WtStraDtSel::WtStraDtSel(const char* id)
	:SelStrategy(id)
{
}

/*!
 * \brief 析构函数
 * 
 * \details 清理策略资源，释放内存
 */
WtStraDtSel::~WtStraDtSel()
{
}

/*!
 * \brief 获取策略名称
 * \return 策略名称字符串
 */
const char* WtStraDtSel::getName()
{
	return "DualThrustSelection";
}

/*!
 * \brief 获取策略工厂名称
 * \return 工厂名称字符串
 */
const char* WtStraDtSel::getFactName()
{
	return FACT_NAME;
}

/*!
 * \brief 初始化策略参数
 * \param cfg 配置参数对象
 * \return 初始化成功返回true，失败返回false
 * 
 * \details 从配置文件中读取选股策略参数：
 *          - days: 历史数据天数，用于计算价格区间
 *          - k1: 上轨系数，控制做多入场敏感度
 *          - k2: 下轨系数，控制做空入场敏感度
 *          - period: K线周期（如"m1", "m5", "d1"）
 *          - count: K线数量，获取历史数据的条数
 *          - stock: 是否为股票交易模式
 *          - codes: 候选品种代码列表，用逗号分隔
 */
bool WtStraDtSel::init(WTSVariant* cfg)
{
	if (cfg == NULL)
		return false;

	_days = cfg->getUInt32("days");
	_k1 = cfg->getDouble("k1");
	_k2 = cfg->getDouble("k2");

	_period = cfg->getCString("period");
	_count = cfg->getUInt32("count");

	_isstk = cfg->getBoolean("stock");

	// 通过逗号分隔确定待选的品种集合
	std::string codes = cfg->getCString("codes");
	auto ayCodes = StrUtil::split(codes, ",");
	for (auto& code : ayCodes)
		_codes.insert(code);

	return true;
}

/*!
 * \brief 策略初始化事件
 * \param ctx 策略上下文
 * 
 * \details 策略启动时调用，执行初始化操作：
 *          - 为所有候选品种订阅Tick数据
 *          - 确保能够接收到实时市场数据
 *          - 为后续的选股分析做准备
 */
void WtStraDtSel::on_init(ISelStraCtx* ctx)
{
	for(auto& code : _codes)
	{
		ctx->stra_sub_ticks(code.c_str());
	}
}

/*!
 * \brief 策略调度事件处理
 * \param ctx 策略上下文
 * \param uDate 当前日期
 * \param uTime 当前时间
 * 
 * \details 定时调度执行选股逻辑，这是策略的核心实现：
 *          
 *          执行流程：
 *          1. 遍历所有候选品种
 *          2. 检查交易时间有效性
 *          3. 获取历史K线数据
 *          4. 计算DualThrust指标
 *          5. 判断交易信号
 *          6. 执行持仓调整
 *          7. 记录交易日志
 *          
 *          选股决策逻辑：
 *          - 空仓状态：根据突破信号建立持仓
 *          - 多仓状态：根据下轨突破平仓
 *          - 空仓状态：根据上轨突破平仓（期货）
 *          
 *          风险控制：
 *          - 交易时间验证：只在交易时段执行
 *          - 数据有效性检查：确保K线数据完整
 *          - 内存管理：及时释放数据资源
 *          - 股票模式限制：股票仅支持做多
 */
void WtStraDtSel::on_schedule(ISelStraCtx* ctx, uint32_t uDate, uint32_t uTime)
{
	for (auto& curCode : _codes)
	{
		WTSSessionInfo* sInfo = ctx->stra_get_sessinfo(curCode.c_str());
		if(!sInfo->isInTradingTime(uTime))
			continue;

		std::string code = curCode;
		if (_isstk)
			code += "-";
		WTSKlineSlice *kline = ctx->stra_get_bars(code.c_str(), _period.c_str(), _count);
		if (kline == NULL)
		{
			// 如果获取数据失败，记录一些日志
			return;
		}

		if (kline->size() == 0)
		{
			kline->release();
			return;
		}

		int32_t trdUnit = 1;
		if (_isstk)
			trdUnit = 100;	// 股票交易单位：100股为1手

		int32_t days = (int32_t)_days;

		// 计算DualThrust指标
		double hh = kline->maxprice(-days, -2);		// N天内最高价
		double ll = kline->minprice(-days, -2);		// N天内最低价

		WTSValueArray* closes = kline->extractData(KFT_CLOSE);
		double hc = closes->maxvalue(-days, -2);	// N天内最高收盘价
		double lc = closes->minvalue(-days, -2);	// N天内最低收盘价
		double curPx = closes->at(-1);				// 当前收盘价
		closes->release();	// !!!注意释放，一定要做

		double openPx = kline->at(-1)->open;		// 今日开盘价
		double highPx = kline->at(-1)->high;		// 今日最高价
		double lowPx = kline->at(-1)->low;			// 今日最低价

		// 计算上下轨
		double upper_bound = openPx + _k1 * (std::max(hh - lc, hc - ll));
		double lower_bound = openPx - _k2 * std::max(hh - lc, hc - ll);

		WTSCommodityInfo* commInfo = ctx->stra_get_comminfo(curCode.c_str());

		double curPos = ctx->stra_get_position(curCode.c_str()) / trdUnit;
		if (decimal::eq(curPos, 0))
		{
			// 空仓状态，寻找入场信号
			if (highPx >= upper_bound)
			{
				ctx->stra_set_position(curCode.c_str(), 1 * trdUnit, "DT_EnterLong");
				// 上轨突破，建立多头持仓
				ctx->stra_log_info(fmt::sprintf("%s 上轨突破%.2f>=%.2f,多仓进入", curCode.c_str(), highPx, upper_bound).c_str());
			}
			else if (lowPx <= lower_bound && !_isstk)
			{
				ctx->stra_set_position(curCode.c_str(), -1 * trdUnit, "DT_EnterShort");
				// 下轨突破，建立空头持仓（仅期货）
				ctx->stra_log_info(fmt::sprintf("%s 下轨突破%.2f<=%.2f,空仓进入", curCode.c_str(), lowPx, lower_bound).c_str());
			}
		}
		//else if(curPos > 0)
		else if (decimal::gt(curPos, 0))
		{
			// 多仓状态，寻找出场信号
			if (lowPx <= lower_bound)
			{
				// 多仓出场
				ctx->stra_set_position(curCode.c_str(), 0, "DT_ExitLong");
				ctx->stra_log_info(fmt::sprintf("%s 下轨突破%.2f<=%.2f,多仓出场", curCode.c_str(), lowPx, lower_bound).c_str());
			}
		}
		//else if(curPos < 0)
		else if (decimal::lt(curPos, 0))
		{
			// 空仓状态，寻找出场信号
			if (highPx >= upper_bound && !_isstk)
			{
				// 空仓出场
				ctx->stra_set_position(curCode.c_str(), 0, "DT_ExitShort");
				ctx->stra_log_info(fmt::sprintf("%s 上轨突破%.2f>=%.2f,空仓出场", curCode.c_str(), highPx, upper_bound).c_str());
			}
		}

		// 内存释放，一定要做
		kline->release();
	}
}

/*!
 * \brief Tick数据事件处理
 * \param ctx 策略上下文
 * \param stdCode 标准合约代码
 * \param newTick 新的Tick数据
 * 
 * \details 接收到新Tick数据时调用：
 *          - 当前选股策略基于K线数据，不处理Tick
 *          - 可扩展实现基于Tick的实时监控功能
 */
void WtStraDtSel::on_tick(ISelStraCtx* ctx, const char* stdCode, WTSTickData* newTick)
{
	// 选股策略主要基于K线数据分析，Tick数据处理可根据需要扩展
}

/*!
 * \brief K线数据事件处理
 * \param ctx 策略上下文
 * \param stdCode 标准合约代码
 * \param period K线周期
 * \param newBar 新的K线数据
 * 
 * \details 处理K线更新事件：
 *          - 可用于实时更新技术指标
 *          - 可触发即时的选股分析
 *          - 当前实现将主要逻辑放在调度事件中
 */
void WtStraDtSel::on_bar(ISelStraCtx* ctx, const char* stdCode, const char* period, WTSBarStruct* newBar)
{
	// K线更新处理逻辑可根据需要扩展，当前主要逻辑在调度事件中执行
}
